<?php
declare (strict_types=1);

namespace think\log\driver;

use think\App;
use think\contract\LogHandlerInterface;
use think\facade\Filesystem;

/**
 * 本地化调试输出到文件
 */
class Cos implements LogHandlerInterface
{
  /**
   * 配置参数
   * @var array
   */
  protected $config = [
      'time_format' => 'c',
      'single' => false,
      'file_size' => 2097152,
      'path' => '',
      'apart_level' => [],
      'json' => false,
      'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
      'format' => '[%s][%s] %s',
  ];

  private $cosDriver; //cos驱动

  // 实例化并传入参数
  public function __construct(App $app, $config = [])
  {

    if (is_array($config)) {
      $this->config = array_merge($this->config, $config);
    }

    if (empty($this->config['format'])) {
      $this->config['format'] = '[%s][%s] %s';
    }

    if (empty($this->config['path'])) {
      $this->config['path'] = 'log/';
    }

    $this->cosDriver = Filesystem::disk('cos');

  }

  /**
   * 日志写入接口
   * @access public
   * @param array $log 日志信息
   * @return bool
   * @throws \Exception
   */
  public function save(array $log): bool
  {

    $path = $this->config['path'] . date('Ym') . DIRECTORY_SEPARATOR;

    $info = [];

    // 日志信息封装
    $time = date($this->config['time_format']);

    foreach ($log as $type => $val) {
      $message = [];
      foreach ($val as $msg) {
        if (!is_string($msg)) {
          $msg = var_export($msg, true);
        }

        $message[] = $this->config['json'] ?
            json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) :
            sprintf($this->config['format'], $time, $type, $msg);
      }

      if (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level'])) {
        // 独立记录的日志级别
        $filename = $path . date('d') . '_' . $type . '.log';
        $this->write($message, $filename);
        continue;
      }

      $info[$type] = $message;
    }

    if ($info) {
      $filename = $path . date('d') . '.log';
      return $this->write($info, $filename);
    }

    return true;
  }

  /**
   * 日志写入
   * @access protected
   * @param array $message 日志信息
   * @param string $destination 日志文件
   * @return bool
   * @throws \Exception
   */
  protected function write(array $message, string $destination): bool
  {
    // 检测日志文件大小，超过配置大小则备份日志文件重新生成
    $this->checkLogSize($destination);

    $info = [];

    foreach ($message as $type => $msg) {
      $info[$type] = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
    }

    $message = implode(PHP_EOL, $info) . PHP_EOL;

    if ($this->cosDriver->has($destination)) {
      $content = $this->cosDriver->read($destination);
      $message = $content . PHP_EOL . $message;
      $this->cosDriver->update($destination, $message); //更新文件内容
    } else {
      $this->cosDriver->write($destination, $message); //写入文件
    }
    return true;
  }

  /**
   * 检查日志文件大小并自动生成备份文件
   * @access protected
   * @param string $destination 日志文件
   * @return void
   * @throws \Exception
   */
  protected function checkLogSize(string $destination): void
  {
    try {
      $fileSize = $this->cosDriver->getSize($destination);
      if (floor($this->config['file_size']) <= $fileSize) {
        $this->cosDriver->rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
      }
    } catch (\Exception $e) {

    }
  }
}
